package com.ruoyi.common.core.utils.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * EasyExcel工具类
 * 基于EasyExcel 3.1.0版本实现Excel导入导出功能
 *
 * @author share
 */
public class EasyexcelUtils {

    /**
     * 导出Excel
     *
     * @param response 响应对象
     * @param list     数据列表
     * @param fileName 文件名
     * @param sheetName 工作表名称
     * @param clazz    实体类类型
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list, String fileName, String sheetName, Class<T> clazz) {
        try {
            setResponseHeader(response, fileName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(getHorizontalCellStyleStrategy())
                    .doWrite(list);
        } catch (Exception e) {
            throw new RuntimeException("导出Excel失败", e);
        }
    }

    /**
     * 导出Excel（默认工作表名）
     *
     * @param response 响应对象
     * @param list     数据列表
     * @param fileName 文件名
     * @param clazz    实体类类型
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list, String fileName, Class<T> clazz) {
        exportExcel(response, list, fileName, "Sheet1", clazz);
    }

    /**
     * 批量导出多个Sheet
     *
     * @param response 响应对象
     * @param fileName 文件名
     * @param map      key: sheet名称, value: 对应sheet的数据和实体类
     */
    public static void exportMultiSheetExcel(HttpServletResponse response, String fileName, Map<String, SheetData<?>> map) {
        try {
            setResponseHeader(response, fileName);
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
            
            for (Map.Entry<String, SheetData<?>> entry : map.entrySet()) {
                String sheetName = entry.getKey();
                SheetData<?> sheetData = entry.getValue();
                
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
                        .head(sheetData.getClazz())
                        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                        .registerWriteHandler(getHorizontalCellStyleStrategy())
                        .build();
                
                excelWriter.write(sheetData.getData(), writeSheet);
            }
            
            excelWriter.finish();
        } catch (Exception e) {
            throw new RuntimeException("导出多Sheet Excel失败", e);
        }
    }

    /**
     * 导入Excel
     *
     * @param inputStream 文件输入流
     * @param clazz       实体类类型
     * @return 数据列表
     */
    public static <T> List<T> importExcel(InputStream inputStream, Class<T> clazz) {
        try {
            return EasyExcel.read(inputStream, clazz, null).sheet().doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 导入Excel（指定sheet）
     *
     * @param inputStream 文件输入流
     * @param clazz       实体类类型
     * @param sheetNo     sheet编号（从0开始）
     * @return 数据列表
     */
    public static <T> List<T> importExcel(InputStream inputStream, Class<T> clazz, int sheetNo) {
        try {
            return EasyExcel.read(inputStream, clazz, null).sheet(sheetNo).doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 导入Excel（指定sheet名称）
     *
     * @param inputStream 文件输入流
     * @param clazz       实体类类型
     * @param sheetName   sheet名称
     * @return 数据列表
     */
    public static <T> List<T> importExcelBySheetName(InputStream inputStream, Class<T> clazz, String sheetName) {
        try {
            return EasyExcel.read(inputStream, clazz, null).sheet(sheetName).doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 导出模板Excel
     *
     * @param response 响应对象
     * @param fileName 文件名
     * @param clazz    实体类类型
     */
    public static <T> void exportTemplate(HttpServletResponse response, String fileName, Class<T> clazz) {
        exportExcel(response, null, fileName, "模板", clazz);
    }

    /**
     * 使用模板填充数据并导出
     *
     * @param response      响应对象
     * @param templateInputStream 模板输入流
     * @param fileName      文件名
     * @param map           单个数据
     * @param listMap       列表数据
     */
    public static void fillTemplate(HttpServletResponse response, InputStream templateInputStream, String fileName, Map<String, Object> map, Map<String, List<?>> listMap) {
        try {
            setResponseHeader(response, fileName);
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                    .withTemplate(templateInputStream)
                    .build();
            
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            
            // 填充单个数据
            if (map != null && !map.isEmpty()) {
                excelWriter.fill(map, writeSheet);
            }
            
            // 填充列表数据
            if (listMap != null && !listMap.isEmpty()) {
                for (Map.Entry<String, List<?>> entry : listMap.entrySet()) {
                    excelWriter.fill(new FillWrapper(entry.getKey(), entry.getValue()), writeSheet);
                }
            }
            
            excelWriter.finish();
        } catch (Exception e) {
            throw new RuntimeException("填充模板导出失败", e);
        }
    }

    /**
     * 设置响应头
     */
    private static void setResponseHeader(HttpServletResponse response, String fileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 防止中文乱码
        String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%");
        response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
    }

    /**
     * 获取表头样式策略
     */
    private static HorizontalCellStyleStrategy getHorizontalCellStyleStrategy() {
        // 表头策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为灰色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 12);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        
        // 内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    /**
     * Sheet数据封装类
     */
    public static class SheetData<T> {
        private List<T> data;
        private Class<T> clazz;

        public SheetData(List<T> data, Class<T> clazz) {
            this.data = data;
            this.clazz = clazz;
        }

        public List<T> getData() {
            return data;
        }

        public Class<T> getClazz() {
            return clazz;
        }
    }
}